package org.jfonia.connect;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.List;

public class Reporter {
	protected int indentStep = 3;
	
	protected PrintWriter out;
	
	public Reporter(PrintWriter out)
	{
		this.out = out;
	}
	
	public Reporter()
	{
		this(new PrintWriter(System.out));
	}
	
	public void report(Object o)
	{
		report(o, 0);
		out.println();
		out.flush();
	}
	
	
	public void report(Object o, int indent)
	{
		if (o instanceof Value)
			reportValue((Value)o, indent);
		else
		{
			out.print(o.toString());
		}
	}
	
	public void reportValue(Value v, int indent)
	{
		out.print("VALUE: ");
		try {
			out.print(v.get().toString());
		}
		catch(Exception e)
		{
			out.print("<<EXCEPTION>>");
		}
		
		indent+=indentStep;
		
		Class<?> c = v.getClass();
		out.print(" [" + c.getSimpleName() +"]");
		printClass(c, v, indent);
		
	}
	
	protected void printClass(Class<?> c, Object v, int indent)
	{
		if (c.equals(BasicNode.class))
			return;
		
		for (Field f: c.getDeclaredFields())
		{
			out.println();
			doIndent(indent);
			printField(c, f, v, indent);
		}
		
		Class<?> sc = c.getSuperclass();
		if (sc != null)
			printClass(sc, v, indent);
	}
	
	protected void printField(Class<?> c, Field f, Object v, int indent)
	{
		// verbose:
		//out.print(c.getSimpleName() + "::" + f.getName() + ": ");
		out.print(f.getName() + ": ");
		
		boolean oneLine = true; 	// try to put list-elements on one line except if they are too long
		try {
			f.setAccessible(true);	// allow private, protected
			Object fv = f.get(v);
			if (fv instanceof List)
			{
				out.print("{");
				for (Object o: (List)fv)
				{
					if (o instanceof Value)
						oneLine = false;
					if (oneLine)
						out.print(" ");
					else {
						out.println();
						doIndent(indent+indentStep);
					}
					report(o, indent+indentStep);
					
				}
				if (oneLine)
					out.print(" } ");
				else
				{
					out.println();
					doIndent(indent);
					out.println("}");
				}
			}
			else	
			{
				report(fv, indent);
			}
				
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
			out.print("<<NO ACCESS>>");
		}
	}
	
	protected void doIndent(int indent)
	{
		for (int i=0; i<indent; i++)
			out.print(" ");
	}
	
	public void flush()
	{
		out.flush();
	}
}
